﻿
using System.Collections.Generic;
using System.ComponentModel;


namespace Boilen.Primitives.Implementers {

    /// <summary>
    /// Implements an accessor with a value.
    /// </summary>
    /// <typeparam name="T">The type of the property.</typeparam>
    public abstract class ValueAccessor<T> : AccessorImplementer<T>, EquatableInterface.ITarget {

        public const string PropertyFieldNamePrefix = "_privateBackingFieldForProperty_";

        protected const string MemberKind = "Property";

        /// <summary>The name of the observe accessor for a property.</summary>
        protected const string ObserveAccessorName = "get";

        /// <summary>The name of the update accessor for a property.</summary>
        protected const string UpdateAccessorName = "set";

        protected readonly string CoerceAction;
        protected readonly string ValidateAction;
        protected readonly string ChangingAction;
        protected readonly string ChangedAction;

        private readonly Dictionary<CompilationSymbol, string> defaultValues_ = new Dictionary<CompilationSymbol, string>( );


        /// <summary>
        /// Gets or sets a value indicating whether the property should be used when determining equality of the parent type.
        /// </summary>
        /// <remarks>
        /// This property is set by <see cref="PartialType.ImplementIEquatable"/>.
        /// </remarks>
        public bool Equatable { get; set; }

        /// <summary>
        /// Gets the set of default values for the property.
        /// </summary>
        public Dictionary<CompilationSymbol, string> DefaultValues { get { return this.defaultValues_; } }

        /// <inheritdoc/>
        protected override string FieldNamePrefix { get { return PropertyFieldNamePrefix; } }


        /// <inheritdoc/>
        protected ValueAccessor( PartialType parent, string name, string description )
            : base( parent, name, description ) {
            this.CoerceAction = this.GetActionName( "Coerce" );
            this.ValidateAction = this.GetActionName( "Validate" );
            this.ChangingAction = this.GetActionName( "Changing" );
            this.ChangedAction = this.GetActionName( "Changed" );
        }


        /// <summary>
        /// Adds the expression to the <see cref="DefaultValues"/> property.
        /// </summary>
        public ValueAccessor<T> AddDefaultExpression( CompilationSymbol condition, string defaultValue ) {
            this.DefaultValues.Add( condition, defaultValue );
            return this;
        }

        /// <summary>
        /// Adds the value to the <see cref="DefaultValues"/> property.
        /// </summary>
        public ValueAccessor<T> AddDefaultValue( CompilationSymbol condition, T defaultValue ) {
            bool silverlight = condition.Equals( CompilationSymbol.Silverlight );
            string stringValue = this.Parent.TypeRepository.GetValueString( defaultValue, silverlight );
            this.AddDefaultExpression( condition, stringValue );
            return this;
        }

        /// <summary>
        /// Assigns the expression used for the <see cref="DefaultValues"/> property.
        /// </summary>
        [Category( "Default Value" )]
        public ValueAccessor<T> SetDefaultExpression( string defaultValue ) {
            this.DefaultValues.Clear( );

            return this.AddDefaultExpression( CompilationSymbol.None, defaultValue );
        }

        /// <summary>
        /// Assigns the value used for the <see cref="DefaultValues"/> property.
        /// </summary>
        [Category( "Default Value" )]
        public ValueAccessor<T> SetDefaultValue( T defaultValue ) {
            string stringValue = this.Parent.TypeRepository.GetValueString( defaultValue, false );
            string silverlightValue = this.Parent.TypeRepository.GetValueString( defaultValue, true );

            if( stringValue == silverlightValue ) {
                this.SetDefaultExpression( stringValue );
            }
            else {
                this.DefaultValues.Clear( );
                this.AddDefaultExpression( CompilationSymbol.NotSilverlight, stringValue );
                this.AddDefaultExpression( CompilationSymbol.Silverlight, silverlightValue );
            }

            return this;
        }


        #region ITarget Members

        /// <inheritdoc/>
        string EquatableInterface.ITarget.PropertyName {
            get { return this.AccessorName; }
        }

        /// <inheritdoc/>
        void EquatableInterface.ITarget.Prepare( EquatableInterface implementer ) { }

        #endregion

    }

}
